<template>
  <VueFlow
    v-if="!initialized"
    v-model:nodes="nodes"
    v-model:edges="edges"
    @nodes-initialized="runLayout"
    class="vue-flow"
  >
    <template #node-custom="node">
      <div class="custom-node" @dblclick="nodeDbClick(node.data)">
        <div :class="['type', node.data.type]">{{ node.label }}</div>
        <div class="content">{{ node.data.name }}</div>
      </div>
    </template>
  </VueFlow>

  <el-dialog
    v-model="dialogVisible"
    class="no-padding"
    :title="dialogTitle"
    destroy-on-close
    fullscreen
    draggable
    append-to-body
    :before-close="beforeDialogClose"
  >
    <Component
      ref="compRef"
      :is="compName"
      :options="dialogData"
      @afterSubmit="afterDialogSubmit"
    />
    <template #footer>
      <el-button
        size="default"
        type="info"
        @click="dialogVisible=false"
      >取消</el-button>
      <el-button
        type="primary"
        size="default"
        :loading="dialogLoading"
        @click="submitDialogForm"
      >提交</el-button>
    </template>
  </el-dialog>
</template>

<script setup>
import { ref, onMounted, watch, markRaw, nextTick } from 'vue';
import { VueFlow, useVueFlow } from '@vue-flow/core';
import api from "@/http/api";
import PageEdit from "@/admin/codeless/pageEdit.vue";
import PageCompEdit from "@/admin/codeless/pageCompEdit.vue";
import DataTableEdit from "@/admin/codeless/dataTableEdit.vue";
import DataFormEdit from "@/admin/codeless/dataFormEdit.vue";
import ScriptGroovyEdit from "@/admin/codeless/scriptGroovyEdit.vue";
import ELK from 'elkjs/lib/elk.bundled';
import { ElMessageBox } from "element-plus";
import "@vue-flow/core/dist/style.css";

const props = defineProps({
  pageId: String,
});

const initialized = ref(false);
const nodes = ref([]);
const edges = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref('');
const dialogData = ref({});
const dialogLoading = ref(false);
const compRef = ref();
const compName = ref();
const { setViewport, getViewport, getNodes, getEdges, fitView } = useVueFlow()

const types = {
  menu: '菜单',
  page: '页面',
  comp: '组件',
  button: '按钮',
  script: '脚本',
  tree: '树',
  table: '表格',
  form: '表单',
  model: '模型',
};

const showGraph = (dataList) => {
  initialized.value = true;
  const nodeList = [];
  const lineList = [];

  const idSet = new Set();
  for (const data of dataList) {
    nodeList.push({
      id: data.id,
      type: 'custom',
      label: types[data.type],
      position: {x: 0, y: 0},
      data: data,
    });

    (data.childrenIds || '').split(',').filter(id => id).forEach(id => {
      addLine(lineList, idSet, data.id, id);
    });
    addLine(lineList, idSet, data.id, data.pageId);
    addLine(lineList, idSet, data.id, data.scriptId);
    addLine(lineList, idSet, data.id, data.modelId);
    addLine(lineList, idSet, data.id, data.treeId);
    addLine(lineList, idSet, data.id, data.tableId);
    addLine(lineList, idSet, data.id, data.formId);
  }

  nodes.value = nodeList;
  edges.value = lineList;
  nextTick(() => initialized.value = false);
};

const addLine = (lineList, idSet, source, target) => {
  if (source && target && !idSet.has(`id_${source}_${target}`)) {
    lineList.push({
      id: `id_${source}_${target}`,
      source: source,
      target: target,
      style: { stroke: '#000', strokeWidth: 1 },
      markerEnd: {
        type: 'arrowclosed',
        color: '#000',
        strokeWidth: 1,
        height: 20,
        width: 20
      }
    });
  }
};

const runLayout = async() => {
  const layout = await new ELK().layout({
    id: 'root',
    children: getNodes.value.map(n => ({ id: n.id, width: n.dimensions?.width || 200, height: n.dimensions?.height || 80 })),
    edges: getEdges.value.map(e => ({ id: e.id, sources: [e.source], targets: [e.target] })),
    layoutOptions: {
      'elk.algorithm': 'layered',
      'elk.direction': 'DOWN',
      'elk.edgeRouting': 'ORTHOGONAL',
      'elk.layered.spacing.nodeNodeBetweenLayers': 80,
      'elk.layered.crossingMinimization.strategy': 'NETWORK_SIMPLEX' // 最强
    }
  });

  layout.children.forEach(l => {
    const node = getNodes.value.find(n => n.id === l.id);
    node.position = { x: l.x, y: l.y }
  });

  await nextTick(async() => {
    await fitView({minZoom: 1, maxZoom: 1});
    const {x, y, zoom} = getViewport();
    await setViewport({x: x < 20 ? 20 : x, y: y < 20 ? 20 : y, zoom: zoom});
  });
};

const nodeDbClick = (data) => {
  if (data.type === 'page') {
    compName.value = markRaw(PageEdit);
    dialogData.value = {id: data.id};
    dialogTitle.value = "页面编辑";
    dialogVisible.value = true;
  } else if (data.type === 'comp' && data.compType !== '可视化') {
    compName.value = markRaw(PageCompEdit);
    dialogData.value = {id: data.id};
    dialogTitle.value = "组件编辑";
    dialogVisible.value = true;
  } else if (data.type === 'table') {
    compName.value = markRaw(DataTableEdit);
    dialogData.value = {id: data.id, modelId: data.modelId};
    dialogTitle.value = "表格编辑";
    dialogVisible.value = true;
  } else if (data.type === 'form') {
    compName.value = markRaw(DataFormEdit);
    dialogData.value = {id: data.id, modelId: data.modelId};
    dialogTitle.value = "表单编辑";
    dialogVisible.value = true;
  } else if (data.type === 'script') {
    compName.value = markRaw(ScriptGroovyEdit);
    dialogData.value = {id: data.id};
    dialogTitle.value = "脚本编辑";
    dialogVisible.value = true;
  }
};

const beforeDialogClose = (done) => {
  if (dialogTitle.value === '脚本编辑' && compRef.value && compRef.value.hasChange()) {
    ElMessageBox.confirm("脚本有变更，请确认是否退出？", "提示", { type: "info" }).then(() => {
      done();
    });
  } else {
    done();
  }
};

const submitDialogForm = () => {
  compRef.value && compRef.value.submitData();
};

const afterDialogSubmit = () => {
  dialogVisible.value = false;
  loadData();
};

const loadData = () => {
  api.post('/system/page/relation/' + props.pageId).then(res => {
    showGraph(res.data || []);
  });
};

watch(() => props.pageId, () => {
  loadData();
});

onMounted(() => {
  loadData();
});
</script>

<style scoped>
.vue-flow path {
  stroke: #faad14;
}
.custom-node {
  min-width: 100px;
  border: 1px solid #000;
  cursor: pointer;
  .type {
    font-size: 16px;
    padding: 5px 0;
    font-weight: bolder;
    text-align: center;
  }
  .content {
    background-color: rgb(238, 238, 238);
    font-size: 14px;
    font-weight: bolder;
    padding: 5px 15px 5px 5px;
    color: #000;
  }
}

.custom-node .menu {
  color: white;
  background: #212121;
}

.custom-node .menu {
  color: orange;
}

.custom-node .page {
  color: white;
  background: #303F9F;
}

.custom-node .comp {
  color: white;
  background: #795548;
}

.custom-node .tree {
  color: white;
  background: #9EB0A2;
}

.custom-node .table {
  color: white;
  background: #43A047;
}

.custom-node .form {
  color: white;
  background: #da3900;
}

.custom-node .model {
  color: white;
  background: #1976D2;
}

.custom-node .button {
  color: white;
  background: #E91E63;
}

.custom-node .script {
  color: white;
  background: #9C27B0;
}
</style>